﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PocketMahjong.Core
{
	public class Board
	{
		#region Internal Classes

		private class PairComparer : IEqualityComparer<Pair>
		{
			public bool Equals(Pair x, Pair y)
			{
				return ((x.Position1 == y.Position1 && x.Position2 == y.Position2)
					|| (x.Position1 == y.Position2 && x.Position2 == y.Position1));
			}

			public int GetHashCode(Pair obj)
			{
				return 0; //TODO: optimize
			}
		}

		private class TileComparer : IEqualityComparer<Tile>
		{
			public bool Equals(Tile x, Tile y)
			{
				return x.Match(y);
			}

			public int GetHashCode(Tile obj)
			{
				return 0;
			}
		}

		#endregion

		#region Properties

		private IArrangeStrategy _arrangement;
		private Layout _layout;
		private Tile[, ,] _tiles;

		public Tile this[Position pos]
		{
			get
			{
				return this[pos.Layer, pos.Row, pos.Column];
			}
			set
			{
				this[pos.Layer, pos.Row, pos.Column] = value;
			}
		}

		public Tile this[int layer, int row, int column]
		{
			get
			{
				if (layer < LayerCount && row < RowCount && column < ColumnCount
					&& layer >= 0 && row >= 0 && column >= 0)
					return _tiles[layer, row, column];
				else
					return null;
			}
			set
			{
				if (layer < LayerCount && row < RowCount && column < ColumnCount
					&& layer >= 0 && row >= 0 && column >= 0)
				{
					ResetStatus();
					_tiles[layer, row, column] = value;
				}
			}
		}

		private List<Position> _selection = new List<Position>();
		public IList<Position> Selection
		{
			get { return _selection; }
		}

		private int _layerCount;
		public int LayerCount { get { return _layerCount; } }

		private int _rowCount;
		public int RowCount { get { return _rowCount; } }

		private int _columnCount;
		public int ColumnCount { get { return _columnCount; } }

		public IEnumerable<Position> AllPositions
		{
			get { return _layout.Positions; }
		}

		private int _tilesCount;
		public int TilesCount
		{
			get
			{
				if (_tilesCount < 0)
					_tilesCount = AllPositions.Where(p => this[p] != null).Count();
				return _tilesCount;
			}
		}

		private int _payersCount;
		public int PayersCount
		{
			get
			{
				if (_payersCount < 0)
					_payersCount = GetPairs().Length;
				return _payersCount;
			}
		}

		#endregion

		public Board(Layout layout, IArrangeStrategy arrangement)
		{
			_arrangement = arrangement;
			_layout = layout;
			_tiles = new Tile[layout.LayerCount, layout.RowCount, layout.ColumnCount];
			_layerCount = layout.LayerCount;
			_rowCount = layout.RowCount;
			_columnCount = layout.ColumnCount;
			ResetStatus();
			Init();
		}

		public Cell[] GetFreePositions()
		{
			return AllPositions.Where(p => this[p] != null && IsFree(p))
				.Select(p => new Cell(p, this[p])).ToArray();

		}

		public bool IsFree(Position pos)
		{
			bool flag1 = true;
			bool flag2 = true;
			for (int row = pos.Row - 1; row <= pos.Row + 1; row++)
				if (this[pos.Layer, row, pos.Column - 2] != null)
					flag1 = false;
			for (int row = pos.Row - 1; row <= pos.Row + 1; row++)
				if (this[pos.Layer, row, pos.Column + 2] != null)
					flag2 = false;

			if (flag1 || flag2)
			{

				if (pos.Layer < LayerCount - 1)
				{
					for (int row = pos.Row - 1; row <= pos.Row + 1; row++)
						for (int col = pos.Column - 1; col <= pos.Column + 1; col++)
							if (this[pos.Layer + 1, row, col] != null)
								return false;
				}
				return true;
			}
			else
				return false;
		}

		public Pair[] GetPairs()
		{
			var positions = GetFreePositions();
			return positions.Join(positions,
				p => p.Tile, p => p.Tile,
				(p, q) => new Pair(p.Position, q.Position), new TileComparer())
				.Where(p => p.Position1 != p.Position2)
				.Distinct(new PairComparer())
				.ToArray();
		}

		public void Init()
		{
			Selection.Clear();
			_arrangement.Arrange(this);
		}

		public void Restart()
		{
			Selection.Clear();
			_arrangement.Restore(this);
		}

		private void ResetStatus()
		{
			_payersCount = _tilesCount = -1;
		}
	}
}
